package com.googlecode.pkchat.pkclient;

import java.awt.*;
import java.awt.event.*;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.*;

import com.apple.eawt.Application;
import com.apple.eawt.ApplicationAdapter;
import com.apple.eawt.ApplicationEvent;
import com.apple.mrj.MRJApplicationUtils;
import javax.swing.JFrame;

import java.awt.datatransfer.*;
import java.awt.Toolkit;

/*******************************************************************************
 *
 * @author Sunmoon Kim
 *
 */
public class ClientGUI implements Runnable, ClipboardOwner {

	private Clipboard clipboard = Toolkit.getDefaultToolkit()
	.getSystemClipboard();

	private JFrame frame, serverFrame;

	private JButton okButton;

	private Container pane;

	private JButton cancelButton;

	private JEditorPane area;

	private JTextField typetext;

	private JTextField iptext;

	private JTextField porttext;

	private JTextField usernametext;

	private JTextField passwordtext;

	private JMenuBar MenuBar;

	private JMenu ConnectionMenu, FileMenu, EditMenu, HelpMenu;

	private JMenuItem NewItem, CloseItem, QuitItem, SaveasItem, CopyItem,
	PasteItem, AboutItem;

	private JList list;

	private JScrollPane chatPanel;
	private JScrollPane rightPanel;

	private Socket socket;

	private DataOutputStream dout;

	private DataInputStream din;

	private String[] users = null;

	private String ChatText = "";

	private Hashtable IMWindows;

	public void run() {
		try {
			while (true) {
				String message = din.readUTF();
				if (message.startsWith("\u2580")) {
					users = message.substring(1).split(" ");
					try {
						list.setListData(users);
					} catch (Exception e) {
					}
				} else if (message.startsWith("\u2582")){
					String[] messages = message.split(" ");
					if(messages.length>2){
						PrivateMessage(message);
					}else System.out.println("Whisper format error!");
				}else {
					ChatText += message + "<br>";
					area.setText(ChatText);
					JScrollBar vbar = chatPanel.getVerticalScrollBar();
					boolean autoScroll = ((vbar.getValue() + vbar
							.getVisibleAmount()) == vbar.getMaximum());
					if (autoScroll)
						area.setCaretPosition(area.getDocument().getLength());
				}
			}
		} catch (IOException ioe) {

		}
	}

	/**
	 * Empty implementation of the ClipboardOwner interface.
	 */
	public void lostOwnership(Clipboard aClipboard, Transferable aContents) {
		// do nothing
	}

	public ClientGUI(){
		IMWindows = new Hashtable();
		CreateWindow();
		CreateWindow2();
	}

	/*
	 * Method to create window
	 */

	public void CreateWindow() {
		frame = new JFrame("PKChat Client");
		frame.setBounds(200, 200, 650, 600);
		pane = frame.getContentPane();
		pane.setLayout(new BorderLayout());

		area = new JEditorPane("text/html","");
		typetext = new JTextField();

		area.addHyperlinkListener(new FollowLinkListener(){


		});

		typetext.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				if(socket!=null){
					String message = ae.getActionCommand();
					if(message.startsWith("/w")){
						PrivateMessage(message);
					}
					processMessage(ae.getActionCommand());
				}
			}
		});

		// Insert a menu bar to window
		MenuBar = new JMenuBar();
		frame.setJMenuBar(MenuBar);

		// Connection Menu
		ConnectionMenu = new JMenu("Connection");
		MenuBar.add(ConnectionMenu);

		NewItem = new JMenuItem("New");
		NewItem.addActionListener(new ButtonListener());
		ConnectionMenu.add(NewItem);

		ConnectionMenu.addSeparator();

		CloseItem = new JMenuItem("Close");
		CloseItem.addActionListener(new ButtonListener());
		ConnectionMenu.add(CloseItem);

		if(System.getProperty("mrj.version") == null){  // Windows
			QuitItem = new JMenuItem("Quit");
			QuitItem.addActionListener(new ButtonListener());
			ConnectionMenu.addSeparator();
			ConnectionMenu.add(QuitItem);
		}
		else{ // Mac
			new MacOSAboutHandler();
		}

		// File Menu
		FileMenu = new JMenu("File");
		MenuBar.add(FileMenu);

		SaveasItem = new JMenuItem("Save Transcript as...");
		SaveasItem.addActionListener(new ButtonListener());
		FileMenu.add(SaveasItem);

		// Edit Menu
		EditMenu = new JMenu("Edit");
		MenuBar.add(EditMenu);

		CopyItem = new JMenuItem("Copy");
		CopyItem.addActionListener(new ButtonListener());
		EditMenu.add(CopyItem);

		PasteItem = new JMenuItem("Paste");
		PasteItem.addActionListener(new ButtonListener());
		EditMenu.add(PasteItem);



		if(System.getProperty("mrj.version") == null){ // Windows
			//			Help menu
			HelpMenu = new JMenu("Help");
			MenuBar.add(HelpMenu);
			AboutItem = new JMenuItem("About");
			HelpMenu.add(AboutItem);
			// Anonymous Inner Class implementation of an action listener

			AboutItem.addActionListener(new ActionListener() {
				AboutDialog myAbout = new AboutDialog();
				public void actionPerformed(ActionEvent e) {
					myAbout.setVisible(true);
				}
			});
		}

		// Paste a panel by GridLayout

		list = new JList();
		list.addListSelectionListener(new ListListener());

		rightPanel = new JScrollPane(list,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
		rightPanel.setMinimumSize(new Dimension(0,150));
		chatPanel = new JScrollPane(area,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

		JSplitPane mainSplit = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,chatPanel,rightPanel);
		mainSplit.setDividerLocation(500);
		mainSplit.setResizeWeight(1);


		pane.add(mainSplit, BorderLayout.CENTER);
		pane.add(typetext, BorderLayout.SOUTH);

		area.setEditable(false);
		frame.setVisible(true);
	}

	class ListListener implements ListSelectionListener {
		public void valueChanged(ListSelectionEvent e) {
		}
	}

	public class ButtonListener implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (e.getSource() == NewItem) {
				serverFrame.setVisible(true);

			} else if (e.getSource() == CloseItem) {
				CloseConnection();
			} else if (e.getSource() == QuitItem) {
				CloseConnection();
				System.exit(1);
			} else if (e.getSource() == CopyItem) {
				clipboard.setContents(new StringSelection(area
						.getSelectedText()), ClientGUI.this);
			} else if (e.getSource() == PasteItem) {
				typetext.setText(typetext.getText() + getClipboardContents());
			} else if (e.getSource() == SaveasItem) {
				saveTranscript();
			} else {
			}
		}
	}

	private class FollowLinkListener implements HyperlinkListener{
		public void hyperlinkUpdate(HyperlinkEvent hle) {
			if (hle.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
				String osName = System.getProperty("os.name");
				String url = hle.getURL().toString();
				try {
					if (osName.startsWith("Mac OS")) {
						Class fileMgr = Class.forName("com.apple.eio.FileManager");
						Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] {String.class});
						openURL.invoke(null, new Object[] {url});
					}
					else if (osName.startsWith("Windows")) Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url);
					else { //assume Unix or Linux
						String[] browsers = { "firefox", "opera", "konqueror", "epiphany", "mozilla", "netscape" };
						String browser = null;
						for (int count = 0; count < browsers.length && browser == null; count++)
							if (Runtime.getRuntime().exec( new String[] {"which", browsers[count]}).waitFor() == 0)
								browser = browsers[count];
						if (browser == null) throw new Exception("Could not find web browser");
						else Runtime.getRuntime().exec(new String[] {browser, url});
					}
				} catch (Exception e) {
					JOptionPane.showMessageDialog(null, "Error opening browser:\n" + e.getLocalizedMessage());
				}
			}
		}
	}

	private void saveTranscript() {
		JFileChooser chooser = new JFileChooser();
		chooser.setSelectedFile(new File("transcript.html"));
		chooser.showSaveDialog(frame);
		File file = chooser.getSelectedFile();
		if (file.exists()) {
			int n = JOptionPane.showConfirmDialog(frame,
					"Are you sure you want to overwrite this file?",
					"Confirm Overwrite", JOptionPane.YES_NO_OPTION);
			if (n == 0)
				writeFile(area.getText(), file);
			else
				saveTranscript();
			return;
		}

		writeFile(area.getText(), file);
	}

	private void writeFile(String toSave, File file) {
		if (file.exists())
			file.delete();
		try {
			FileWriter out = new FileWriter(file);
			out.write(toSave);
			out.close();
		} catch (IOException e) {
			JOptionPane.showMessageDialog(frame,
					"There was an error writing to this file.", "Error",
					JOptionPane.ERROR_MESSAGE);
		}
	}

	/**
	 * Get the String residing on the Clipboard.
	 *
	 * @return any text found on the Clipboard; if none found, return an empty
	 *         String.
	 */
	public String getClipboardContents() {
		String result = "";
		// odd: the Object param of getContents is not currently used
		Transferable contents = clipboard.getContents(null);
		boolean hasTransferableText = (contents != null)
		&& contents.isDataFlavorSupported(DataFlavor.stringFlavor);
		if (hasTransferableText) {
			try {
				result = (String) contents
				.getTransferData(DataFlavor.stringFlavor);
			} catch (UnsupportedFlavorException ex) {
				// highly unlikely since we are using a standard DataFlavor
				System.out.println(ex);
				ex.printStackTrace();
			} catch (IOException ex) {
				System.out.println(ex);
				ex.printStackTrace();
			}
		}
		return result;
	}

	public void CreateWindow2() {
		serverFrame = new JFrame("Connect to server...");
		serverFrame.setBounds(100, 100, 200, 150);
		serverFrame.setResizable(false);
		serverFrame.setVisible(false);
		Container pane = serverFrame.getContentPane();
		pane.setLayout(new BorderLayout());

		// Center Panel - set labels and text fields
		JPanel leftPanel = new JPanel(new GridLayout(4, 2));

		iptext = new JTextField();
		porttext = new JTextField();
		usernametext = new JTextField();
		passwordtext = new JTextField();

		leftPanel.add(new JLabel("IP:", SwingConstants.CENTER));
		leftPanel.add(iptext);

		leftPanel.add(new JLabel("PORT:", SwingConstants.CENTER));
		leftPanel.add(porttext);

		leftPanel.add(new JLabel("USERNAME:", SwingConstants.CENTER));
		leftPanel.add(usernametext);

		leftPanel.add(new JLabel("PASSWORD:", SwingConstants.CENTER));
		leftPanel.add(passwordtext);

		// Bottom Panel - set buttons
		JPanel bottomPanel = new JPanel();

		okButton = new JButton("Ok");
		okButton.addActionListener(new ButtonListener2());
		bottomPanel.add(okButton);

		cancelButton = new JButton("Cancel");
		cancelButton.addActionListener(new ButtonListener2());
		bottomPanel.add(cancelButton);

		pane.add(bottomPanel, BorderLayout.SOUTH);
		pane.add(leftPanel, BorderLayout.CENTER);
	}

	// DEFINITION OF INNER CLASSES
	public class ButtonListener2 implements ActionListener {
		public void actionPerformed(ActionEvent e) {
			if (e.getSource() == okButton) {
				serverFrame.setVisible(false);
				String ip = iptext.getText();
				int port = Integer.parseInt(porttext.getText());
				String username = usernametext.getText();
				String password = passwordtext.getText();
				StartConnection(ip, port, username, password);
			} else if (e.getSource() == cancelButton) {
				serverFrame.setVisible(false);
			}
		}
	}

	private void PrivateMessage(String message){
		String[] messages = message.split(" ");
		String fromUser = messages[1];
		String initialMessage;
		ImWindow myWindow = null;

		// Parse out the initial message
		if(messages.length<3) initialMessage = "";
		else if(message.startsWith("/w")){ // outgoing whisper
			for(int i=2;i>0;i--) message = message.substring(message.indexOf(' ')+1);
			initialMessage = "You: " + message + "<br>";
		}else{ // incoming whisper
			for(int i=2;i>0;i--) message = message.substring(message.indexOf(' ')+1);
			initialMessage = fromUser + ": " + message + "<br>";
		}

		// Search for an existing window
		for(Enumeration e = IMWindows.elements();e.hasMoreElements();){
			ImWindow tempWindow = (ImWindow) e.nextElement();
			if(tempWindow.getName().equalsIgnoreCase(fromUser)) myWindow = tempWindow;
		}

		if(myWindow != null){
			myWindow.AppendMessage(initialMessage);
		}
		else{
			myWindow = new ImWindow(fromUser);
			myWindow.AppendMessage(initialMessage);
			IMWindows.put(fromUser, myWindow);
		}
	}

	private void processMessage(String message) {
		try {
			dout.writeUTF(message);
		} catch (IOException e) {
			System.out.println(e);
		}
		typetext.setText("");
	}

	public static void main(String[] args) {
		ClientGUI a = new ClientGUI();
	}

	private void StartConnection(String ip, int port, String username,
			String password) {
		CloseConnection();
		ChatText += "Connecting to " + ip + ":" + port + "...";
		area.setText(ChatText);
		try {

			// Initiate the connection
			socket = new Socket(ip, port);

			// We got a connection! Tell the world
			// System.out.println( "connected to "+socket );

			// Let's grab the streams and create DataInput/Output streams
			// from them
			din = new DataInputStream(socket.getInputStream());
			dout = new DataOutputStream(socket.getOutputStream());

			// Change the default name
			//dout.writeUTF("\u2583 "+username);

			// Start a background thread for receiving messages
			new Thread(this).start();
		} catch (IOException ie) {
			ChatText += "failed.<br>";
			area.setText(ChatText);
			return;
		}
		ChatText += "connected.<br>";
		area.setText(ChatText);
	}

	private void CloseConnection() {
		try {
			if (din != null)
				din.close();
			if (dout != null)
				dout.close();
			if (socket != null) {
				socket.close();
				ChatText += "Connection closed.<br>";
				area.setText(ChatText);
			}
		} catch (IOException e) {
			System.out
			.println("There was an IO Exception while closing the connection.");
		}
		list.setListData(new Vector());
	}

	private class ImWindow extends JFrame implements ActionListener {
		private String name;

		private Container pane;

		private JEditorPane area;

		private JTextField text;

		String chatText = "";

		public ImWindow(String name) {
			// Set up the window
			super("Private messages with " + name);
			this.name = name;
			this.setBounds(100, 100, 200, 200);

			// Initialize some components
			pane = this.getContentPane();
			area = new JEditorPane("text/html",chatText);
			area.addHyperlinkListener(new FollowLinkListener());
			area.setEditable(false);
			text = new JTextField();
			JScrollPane chatPane = new JScrollPane(area,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

			// Set up an action listener
			text.addActionListener(this);

			// Arrange the components
			pane.setLayout(new BorderLayout());
			pane.add(chatPane, BorderLayout.CENTER);
			pane.add(text, BorderLayout.SOUTH);

			// Make the window visible
			this.setVisible(true);
		}

		public void AppendMessage(String message) {
			chatText += message;
			area.setText(chatText);
			JScrollBar vbar = chatPanel.getVerticalScrollBar();
			boolean autoScroll = ((vbar.getValue() + vbar
					.getVisibleAmount()) == vbar.getMaximum());
			if (autoScroll)
				area.setCaretPosition(area.getDocument().getLength());
		}

		private void SendMessage(String message) {
			try {
				dout.writeUTF("/w "+name+" "+message);
			} catch (IOException e) {
				System.out.println(e);
			}
			chatText += "You: " + message + "<br>";
			area.setText(chatText);
			text.setText("");
		}

		public void actionPerformed(ActionEvent arg0) {
			SendMessage(text.getText());
		}

		public String getName() {
			return name;
		}
	}
}
